#include "chapter17.h"

std::ostream &print(std::ostream &os, QueryResultTuple result)
{
    cout << "The word " << std::get<0>(result) << " has appeared " << std::get<1>(result) << " times.\n";
    cout << "Here are detail info:\n";
    for (auto line : *(std::get<3>(result)))
    {
        cout << "( " << line << " )"
             << "   " << std::get<2>(result)->at(line) << "\n";
    }
    return os;
}

TextQueryUseTuple::TextQueryUseTuple(std::ifstream &in_file)
{
    std::vector<std::string> lines;
    std::map<std::string, int> wds_cnt;
    std::map<std::string, std::set<int>> word_lines_map;

    std::string temp("");
    int line = 0;

    while (std::getline(in_file, temp))
    {
        lines.push_back(temp);
        std::istringstream in_str(temp);
        std::string word;
        while (in_str >> word)
        {
            ++(wds_cnt.insert({word, 0}).first->second);
            word_lines_map[word].insert(line);
        }
        ++line;
    }

    contents = std::make_shared<std::vector<std::string>>(lines);
    words_map = std::make_shared<std::map<std::string, std::set<int>>>(word_lines_map);
    word_count = std::make_shared<std::map<std::string, int>>(wds_cnt);

#ifndef NDEBUG
    cout << "print TextQuery's contents: \n";
    std::ostream_iterator<std::string> it_cout1(cout, "\n");
    std::copy(contents->begin(), contents->end(), it_cout1);
    cout << "print TextQuery's word count: \n";
    for (auto &item : *(word_count))
    {
        cout << item.first << ", " << item.second << "||";
    }
    cout << "\n"
         << "print TextQuery's words map: \n";
    std::ostream_iterator<int> it_cout2(cout, ",");
    for (auto &item : *(words_map))
    {
        cout << " || " << item.first << " ";
        std::copy(item.second.begin(), item.second.end(), it_cout2);
    }
    cout << "\n";
#endif
}

QueryResultTuple TextQueryUseTuple::query(const std::string &word) const
{
    auto iter = word_count->find(word);
    if (iter == word_count->end())
    {
        throw std::runtime_error("Not found\n");
    }
    std::shared_ptr<std::set<int>> line_set = std::make_shared<std::set<int>>(words_map->find(word)->second);
    int freq = iter->second;

#ifndef NDEBUG
    cout << "\n"
         << "print QueryResultTuple's word and frequency: \n";
    cout << word << " , " << freq;
    cout << "\n"
         << "print QueryResultTuple's line set: \n";
    std::ostream_iterator<int> it_cout2(cout, ",");
    std::copy(line_set->begin(), line_set->end(), it_cout2);
    cout << "\n";
#endif

    QueryResultTuple result{word, freq, contents, line_set};
    return result;
}

void runQueriesUseTuple(std::ifstream &infile)
{
    // infile is an ifstream that is the file we want to query
    TextQueryUseTuple tq(infile); // store the file and build the query map
    // // iterate with the user: prompt for a word to find and print results
    while (true)
    {
        cout << "enter word to look for, or q to quit: ";

        std::string s;
        // stop if we hit end-of-file on the input or if a 'q' is entered
        if (!(cin >> s) || s == "q")
            break;
        // run the query and print the results
        try
        {
            print(cout, tq.query(s)) << endl;
        }
        catch (std::runtime_error e)
        {
            cout << "# ERR: Exception in " << __FILE__;
            cout << "(" << __FUNCTION__ << ") on line "
                 << __LINE__ << endl;
            cout << "# ERR: " << e.what();
            break;
        }
    }
}

int exercise17_1()
{
    std::tuple<int, int, int> test{10, 20, 30};
    cout << "test{10, 20, 30} \n get<0>" << std::get<0>(test)
         << "\n get<1>" << std::get<1>(test)
         << "\n get<2>" << std::get<2>(test);
    return 0;
}

int exercise17_2()
{
    std::tuple<std::string, std::vector<std::string>, std::pair<std::string, int>>
        test{"this is string", {"this", "is", "string", "vector"}, {"pair string", 1}};

    cout << "std::tuple<std::string, std::vector<std::string>, std::pair<std::string, int>> \n get<0>" << std::get<0>(test)
         << "\n get<2>" << std::get<2>(test).first << "," << std::get<2>(test).second;
    cout << "\n get<1> :";
    rangeForContainer(std::get<1>(test));
    return 0;
}

int exercise17_3()
{
    std::ifstream inFile("/home/tian/projects/hellocpp/src/CppPrimer5th/chapter12/data/input_text.txt");
    if (inFile.is_open())
    {
        runQueriesUseTuple(inFile);
    }
    inFile.close();
    return 0;
}

vector<Sales_data> build_store(const string &s)
{
    Sales_data item;
    vector<Sales_data> ret;
    std::ifstream is(s);
    while (read(is, item))
        ret.push_back(item);
    sort(ret.begin(), ret.end(), lt); // need sort for equal_range to work
    return ret;
}

vector<matches> findBook(const vector<vector<Sales_data>> &files,
                         const string &book)
{
    vector<matches> ret; // initially empty
    // for each store find the range of matching books, if any
    for (auto it = files.cbegin(); it != files.cend(); ++it)
    {
        // find the range of Sales_data that have the same ISBN
        auto found = equal_range(it->cbegin(), it->cend(),
                                 book, compareIsbn);
        if (found.first != found.second) // this store had sales
            // remember the index of this store and the matching range
            ret.push_back(make_tuple(it - files.cbegin(),
                                     found.first, found.second));
    }
    return ret; // empty if no matches found
}

void reportResults(istream &in, ostream &os,
                   const vector<vector<Sales_data>> &files)
{
    string s; // book to look for
    while (in >> s)
    {
        auto trans = findBook(files, s); // stores that sold this book
        if (trans.empty())
        {
            cout << s << " not found in any stores" << endl;
            continue; // get the next book to look for
        }
        for (const auto &store : trans) // for every store with a sale
            // get<n> returns the specified member from the tuple in store
            os << "store " << std::get<0>(store) << " sales: "
               << std::accumulate(std::get<1>(store), std::get<2>(store),
                                  Sales_data(s))
               << endl;
    }
}

vector<pair_matches> findBookUsePair(const vector<vector<Sales_data>> &files,
                                     const string &book)
{
    vector<pair_matches> ret; // initially empty
    // for each store find the range of matching books, if any
    for (auto it = files.cbegin(); it != files.cend(); ++it)
    {
        // find the range of Sales_data that have the same ISBN
        auto found = equal_range(it->cbegin(), it->cend(),
                                 book, compareIsbn);
        if (found.first != found.second) // this store had sales
            // remember the index of this store and the matching range
            ret.push_back(std::make_pair(it - files.cbegin(),
                                         std::make_pair(found.first, found.second)));
    }
    return ret; // empty if no matches found
}

void reportResultsUsePair(istream &in, ostream &os,
                          const vector<vector<Sales_data>> &files)
{
    string s; // book to look for
    while (in >> s)
    {
        auto trans = findBookUsePair(files, s); // stores that sold this book
        if (trans.empty())
        {
            cout << s << " not found in any stores" << endl;
            continue; // get the next book to look for
        }
        for (const auto &store : trans) // for every store with a sale
            // get<n> returns the specified member from the tuple in store
            os << "store " << store.first << " sales: "
               << std::accumulate(store.second.first, store.second.second,
                                  Sales_data(s))
               << endl;
    }
}

vector<obj_matches> findBookUseObj(const vector<vector<Sales_data>> &files,
                                   const string &book)
{
    vector<obj_matches> ret; // initially empty
    // for each store find the range of matching books, if any
    for (auto it = files.cbegin(); it != files.cend(); ++it)
    {
        // find the range of Sales_data that have the same ISBN
        auto found = equal_range(it->cbegin(), it->cend(),
                                 book, compareIsbn);
        if (found.first != found.second) // this store had sales
        {                                // remember the index of this store and the matching range
            obj_matches temp(it - files.cbegin(), found.first, found.second);
            ret.push_back(temp);
        }
    }
    return ret; // empty if no matches found
}

void reportResultsUseObj(istream &in, ostream &os,
                         const vector<vector<Sales_data>> &files)
{
    string s; // book to look for
    while (in >> s)
    {
        auto trans = findBookUseObj(files, s); // stores that sold this book
        if (trans.empty())
        {
            cout << s << " not found in any stores" << endl;
            continue; // get the next book to look for
        }
        for (const auto &store : trans) // for every store with a sale
            // get<n> returns the specified member from the tuple in store
            os << "store " << store.idx << " sales: "
               << std::accumulate(store.cbegin, store.cend,
                                  Sales_data(s))
               << endl;
    }
}

int exercise17_4(int argc, char **argv)
{
    vector<vector<Sales_data>> files;
    for (int cnt = 1; cnt != argc; ++cnt)
        files.push_back(build_store(argv[cnt]));

    ifstream in("../data/findbook.in"); // ISBNs to search for
    reportResults(in, cout, files);
    return 0;
}

int exercise17_5(int argc, char **argv)
{
    vector<vector<Sales_data>> files;
    for (int cnt = 1; cnt != argc; ++cnt)
        files.push_back(build_store(argv[cnt]));

    ifstream in("../data/findbook.in"); // ISBNs to search for
    reportResultsUsePair(in, cout, files);
    return 0;
}

int exercise17_6(int argc, char **argv)
{
    vector<vector<Sales_data>> files;
    for (int cnt = 1; cnt != argc; ++cnt)
        files.push_back(build_store(argv[cnt]));

    ifstream in("../data/findbook.in"); // ISBNs to search for
    reportResultsUseObj(in, cout, files);
    return 0;
}

int exercise17_7()
{
    return 0;
}

int exercise17_8()
{
    return 0;
}

int exercise17_9()
{
    std::bitset<64> bitvec(32);
    cout << bitvec << endl;

    std::bitset<32> bv1(1010101);
    cout << bv1 << endl;

    string bstr;
    cin >> bstr; // 00000001000000000000
    std::bitset<8> bv2(bstr);
    cout << bv2 << endl; // 00000001
    return 0;
}

int exercise17_10()
{
    std::bitset<32> bv1;
    unsigned int idx(0);

    // 1 2 3 5 8 13 21
    while (cin >> idx && (idx < 31))
    {
        bv1.set(idx);
    }
    cout << "\n"
         << bv1 << endl;
    // 00000000001000000010000100101110
    return 0;
}

int exercise17_11()
{
    answers<10> ans_of_10;

    ans_of_10.ansSet.set(9);
    cout << ans_of_10.ansSet << endl;

    answers<100> ans_of_100;

    ans_of_100.ansSet.set(99);
    cout << ans_of_100.ansSet << endl;

    return 0;
}

int exercise17_12()
{
    answers<10> ans_of_10;

    ans_of_10.setAns(5, true);
    ans_of_10.setAns(8, false);
    ans_of_10.setAns(9, false);

    cout << ans_of_10.ansSet << endl;
    // 0000100000
    return 0;
}

int exercise17_13()
{
    answers<10> ans_of_10;

    ans_of_10.setAll(65);
    cout << ans_of_10.ansSet << endl;
    // 0001000001
    return 0;
}

int exercise17_14()
{
    return 0;
}

int exercise17_15()
{
    return 0;
}

int exercise17_16()
{
    return 0;
}

int exercise17_17()
{
    return 0;
}

int exercise17_18()
{
    return 0;
}

int exercise17_19()
{
    return 0;
}

int exercise17_20()
{
    return 0;
}

int exercise17_21()
{
    return 0;
}

int exercise17_22()
{
    return 0;
}

int exercise17_23()
{
    return 0;
}

int exercise17_24()
{
    return 0;
}

int exercise17_25()
{
    return 0;
}

int exercise17_26()
{
    return 0;
}

int exercise17_27()
{
    return 0;
}

int exercise17_28()
{
    return 0;
}

int exercise17_29()
{
    return 0;
}

int exercise17_30()
{
    return 0;
}

int exercise17_31()
{
    return 0;
}

int exercise17_32()
{
    return 0;
}

int exercise17_33()
{
    return 0;
}

int exercise17_34()
{
    return 0;
}

int exercise17_35()
{
    return 0;
}

int exercise17_36()
{
    return 0;
}

int exercise17_37()
{
    return 0;
}

int exercise17_38()
{
    return 0;
}

int exercise17_39()
{
    return 0;
}
